package com.freewater.springboot.demo.aop.pointcuts;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.aop.aspectj.annotation.AspectJProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.ClassUtils;

/**
 * args
 * 格式说明
 * args(参数类型列表)匹配当前执行的方法传入的参数是否为args中指定的类型；参数类型列表中的参数必须是类型全限定名，不支持通配符；args属于动态切入点，也就是执行方法的时候进行判断的，开销非常大，非特殊情况最好不要使用。
 * <p>
 * args(String) //    方法个数为1，类型是String
 * args(*,String) //  方法参数个数2，第2个是String类型
 * args(..,String) // 方法个数不限制，最后一个必须是String
 * <p>
 * 链接：<a href="https://juejin.cn/post/7077425331954515999">Spring系列25：Spring AOP 切点详解</a>
 */

@Aspect
public class PointcutArgs {

    interface I1 {
        void m(Object name);
    }

    static class C1 implements I1 {

        @Override
        public void m(Object name) {
            String type = name.getClass().getName();
            System.out.println("C1 m() 参数类型 " + type);
        }
    }

    // 匹配方法参数个数1且类型是必须是String
    @Pointcut("args(String)")
    public void pc() {
    }

    @Before("pc()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before: " + joinPoint);
    }

    // 结果：参数类型传递是String时候增强了，而Long的时候没有执行增强方法。
    public static void main(String[] args) {
        C1 target = new C1();
        AspectJProxyFactory proxyFactory = new AspectJProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.setProxyTargetClass(true);
        // 获取C1上所有接口 spring工具类提供的方法
        Class<?>[] allInterfaces = ClassUtils.getAllInterfaces(target);
        // 设置代理接口
        proxyFactory.setInterfaces(allInterfaces);
        // 添加切面
        proxyFactory.addAspect(PointcutArgs.class);
        // 获取代理
        I1 proxy = proxyFactory.getProxy();
        // 调用方法
        proxy.m("xxxx");
        proxy.m(100L);
        System.out.println("JDK代理? " + AopUtils.isJdkDynamicProxy(proxy));
        System.out.println("CGLIB代理? " + AopUtils.isCglibProxy(proxy));
        //判断代理对象是否是C1类型的
        System.out.println(C1.class.isAssignableFrom(target.getClass()));
    }
}
